/*
 * Copyright (C) 2012 CyberAgent
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.gpuimage;

import android.annotation.SuppressLint;
import android.opengl.GLES20;

import com.common.base.log.DLog;
import com.android.gpuimage.util.TextureRotationUtil;
//import com.photoeditor.demo.ui.widget.image.multisticker.GPUImageStickerFilter;
//import com.photoeditor.demo.ui.widget.image.multisticker.HueStickerFilter;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import static com.android.gpuimage.GPUImageRenderer.CUBE;


/**
 * Resembles a filter that consists of multiple filters applied after each
 * other.
 */
public class GPUImageFilterGroup extends GPUImageFilter {

    protected List<GPUImageFilter> mFilters;
    protected List<GPUImageFilter> mMergedFilters;
    private int[] mFrameBuffers;
    private int[] mFrameBufferTextures;

    private final FloatBuffer mGLCubeBuffer;
    private final FloatBuffer mGLTextureBuffer;
    private final FloatBuffer mGLTextureFlipBuffer;

    private long mTimestamp;

    //暂存加入的filter
    private GPUImageFilter mFilterAdded;

    /**
     * Instantiates a new GPUImageFilterGroup with no filters.
     */
    public GPUImageFilterGroup() {
        this(null);
    }

    /**
     * Instantiates a new GPUImageFilterGroup with the given filters.
     *
     * @param filters the filters which represent this filter
     */
    public GPUImageFilterGroup(List<GPUImageFilter> filters) {
        mFilters = filters;
        if (mFilters == null) {
            mFilters = new ArrayList<GPUImageFilter>();
        } else {
            updateMergedFilters();
        }

        mGLCubeBuffer = ByteBuffer.allocateDirect(GPUImageRenderer.CUBE.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mGLCubeBuffer.put(GPUImageRenderer.CUBE).position(0);

        mGLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mGLTextureBuffer.put(TextureRotationUtil.TEXTURE_NO_ROTATION).position(0);

        float[] flipTexture = TextureRotationUtil.getRotation(Rotation.NORMAL, false, true);
        mGLTextureFlipBuffer = ByteBuffer.allocateDirect(flipTexture.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mGLTextureFlipBuffer.put(flipTexture).position(0);
    }

    public void addFilter(GPUImageFilter aFilter) {
        if (aFilter == null) {
            return;
        }
        mFilters.add(aFilter);
        updateMergedFilters();
    }

    public void addSingleFilter(GPUImageFilter aFilter) {
        if (aFilter == null) {
            return;
        }
        mFilters.add(aFilter);
        mFilterAdded = aFilter;
        updateMergedFilters();
    }

    public void addFilter(int index, GPUImageFilter aFilter) {
        if (aFilter == null) {
            return;
        }
        mFilters.add(index, aFilter);
        updateMergedFilters();
    }

    public void addFilterOnly(int index, GPUImageFilter aFilter) {
        if (aFilter == null) {
            return;
        }
        mFilters.add(index, aFilter);
    }

    public GPUImageFilter getFilter(int index) {
        if (mFilters == null || mFilters.size() == 0) {
            return null;
        }
        return mFilters.get(index);
    }

    public void addFilter(List<GPUImageFilter> filters) {
        if (filters == null) {
            return;
        }
        mFilters.addAll(filters);
        updateMergedFilters();
    }

    public int getFilterCount() {
        if (mFilters == null || mFilters.size() == 0) {
            return 0;
        } else {
            return mFilters.size();
        }
    }

    public void removeFilter(GPUImageFilter aFilter) {
        if (aFilter == null) {
            return;
        }
        mFilters.remove(aFilter);
        updateMergedFilters();
    }

    public void removeFilter(List<GPUImageFilter> filters) {
        if (filters == null || filters.size() == 0) {
            return;
        }
        mFilters.removeAll(filters);
        updateMergedFilters();
    }

    /*
     * (non-Javadoc)
     * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onInit()
     */
    @Override
    public void onInit() {
        super.onInit();
        for (GPUImageFilter filter : mFilters) {
            filter.init();
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDestroy()
     */
    @Override
    public void onDestroy() {
        destroyFramebuffers();
        for (GPUImageFilter filter : mFilters) {
            filter.destroy();
        }
        super.onDestroy();
    }

    private void destroyFramebuffers() {
        if (mFrameBufferTextures != null) {
            GLES20.glDeleteTextures(mFrameBufferTextures.length, mFrameBufferTextures, 0);
            mFrameBufferTextures = null;
        }
        if (mFrameBuffers != null) {
            GLES20.glDeleteFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);
            mFrameBuffers = null;
        }
    }

    /*
     * (non-Javadoc)
     * @see
     * jp.co.cyberagent.android.gpuimage.GPUImageFilter#onOutputSizeChanged(int,
     * int)
     */
    @Override
    public void onOutputSizeChanged(final int width, final int height) {
        super.onOutputSizeChanged(width, height);
        if (mFrameBuffers != null) {
            destroyFramebuffers();
        }

        int size = mFilters.size();
        for (int i = 0; i < size; i++) {
            mFilters.get(i).onOutputSizeChanged(width, height);
        }

        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            size = mMergedFilters.size();
            mFrameBuffers = new int[size - 1];
            mFrameBufferTextures = new int[size - 1];

            for (int i = 0; i < size - 1; i++) {
                GLES20.glGenFramebuffers(1, mFrameBuffers, i);
                GLES20.glGenTextures(1, mFrameBufferTextures, i);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i]);
                GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
                        GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                        GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

                GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]);
                GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                        GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i], 0);

                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
                GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
            }
        }
    }

    public void onOutputSizeChangedAfterSingleFilterAdded(final int width, final int height) {
        super.onOutputSizeChanged(width, height);
        //第一次添加filter
        if (mFrameBuffers == null) {
            onOutputSizeChanged(width, height);
        } else {
            if (mFilterAdded != null) {
                mFilterAdded.onOutputSizeChanged(width, height);
                int size = mMergedFilters.size();
                int[] frameBuffers = new int[size - 1];
                int[] frameBufferTextures = new int[size - 1];
                int i = 0;
                if (mFrameBuffers != null && mFrameBuffers.length > 0) {
                    int lastLen = mFrameBuffers.length;
                    for (i = 0; i < lastLen; ++i) {
                        frameBuffers[i] = mFrameBuffers[i];
                        frameBufferTextures[i] = mFrameBufferTextures[i];
                    }
                }
                for (; i < size - 1; i++) {
                    GLES20.glGenFramebuffers(1, frameBuffers, i);
                    GLES20.glGenTextures(1, frameBufferTextures, i);
                    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTextures[i]);
                    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
                            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
                    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                            GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
                    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                            GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
                    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                            GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
                    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                            GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

                    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[i]);
                    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                            GLES20.GL_TEXTURE_2D, frameBufferTextures[i], 0);

                    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
                    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
                }

                mFrameBuffers = frameBuffers;
                mFrameBufferTextures = frameBufferTextures;
            }
        }
    }

    @Override
    public void setImageSize(int width, int height) {
        super.setImageSize(width, height);
        int size = mFilters.size();
        for (int i = 0; i < size; i++) {
            mFilters.get(i).setImageSize(width, height);
        }
    }

    /**
     * 设置当前的时间参数，视频gif需要用到
     */
    public void updateTimestamp(long timestamp) {
        mTimestamp = timestamp;
        if (mMergedFilters != null) {
            int size = mMergedFilters.size();
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                if (filter instanceof GPUImageDynamicFilter) {
                    ((GPUImageDynamicFilter) filter).updateTimestamp(mTimestamp);
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDraw(int,
     * java.nio.FloatBuffer, java.nio.FloatBuffer)
     */
    @SuppressLint("WrongCall")    
    @Override
    public void onDraw(final int textureId, final FloatBuffer cubeBuffer,
                       final FloatBuffer textureBuffer) {
        runPendingOnDrawTasks();
        if (!isInitialized() || mFrameBuffers == null || mFrameBufferTextures == null) {
            return;
        }
        DLog.d("zoomLog", "glClear() called");
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        if (mMergedFilters != null) {
            int size = mMergedFilters.size();
            int previousTexture = textureId;
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                boolean isNotLast = i < size - 1;
                if (isNotLast) {
                    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]);
//                    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//                    GLES20.glClearColor(0, 1, 0, 0);
                }

                if (i > 1 &&
                        (filter instanceof GPUImageSelectiveBlurFilter
                        || filter instanceof GPUImageTiltShiftSubFilter)) {
                    GPUImageTwoInputFilter selectiveBlurFilter = (GPUImageTwoInputFilter) filter;
                    if (i == 2) {
                         {
                            selectiveBlurFilter.setTexture2(previousTexture, Rotation.NORMAL, false, false);
                        }
                        selectiveBlurFilter.onDraw(textureId, cubeBuffer, textureBuffer);
                    } else {
                        {
                            selectiveBlurFilter.setTexture2(previousTexture, Rotation.NORMAL, false, true);
                        }
                        selectiveBlurFilter.onDraw(mFrameBufferTextures[i - 3], mGLCubeBuffer, mGLTextureFlipBuffer);
                    }
                } else {
                    DLog.d("groupLog", "filter type: " + filter + "   i= " + i);
                    if (i == 0 && !(filter instanceof GPUImageHueFilter)) {
                        filter.onDraw(previousTexture, cubeBuffer, textureBuffer);
                    } else if (i == 0 && (filter instanceof GPUImageHueFilter)) {
                        filter.onDraw(previousTexture, mGLCubeBuffer, mGLTextureFlipBuffer);
                    } else {
                        {
                            filter.onDraw(previousTexture, mGLCubeBuffer, mGLTextureFlipBuffer);
                        }
                    }
                }

                if (isNotLast) {
                    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
                    // 线上这里有报空指针错误
                    if (mFrameBufferTextures != null && i < mFrameBufferTextures.length) {
                        previousTexture = mFrameBufferTextures[i];
                    }
                }
            }
        }
     }

    /**
     * Gets the filters.
     *
     * @return the filters
     */
    public List<GPUImageFilter> getFilters() {
        return mFilters;
    }

    public List<GPUImageFilter> getMergedFilters() {
        return mMergedFilters;
    }

    public void updateMergedFilters() {
        if (mFilters == null) {
            return;
        }

        if (mMergedFilters == null) {
            mMergedFilters = new ArrayList<GPUImageFilter>();
        } else {
            mMergedFilters.clear();
        }

        List<GPUImageFilter> filters;
        for (GPUImageFilter filter : mFilters) {
            if (filter instanceof GPUImageFilterGroup) {
                ((GPUImageFilterGroup) filter).updateMergedFilters();
                filters = ((GPUImageFilterGroup) filter).getMergedFilters();
                if (filters == null || filters.isEmpty())
                    continue;
                mMergedFilters.addAll(filters);
                continue;
            }
            mMergedFilters.add(filter);
        }
    }

    @Override
    public void setRotation(Rotation rotation, boolean flipHorizontal, boolean flipVertical) {
        super.setRotation(rotation, flipHorizontal, flipVertical);
        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            int size = mMergedFilters.size();
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                if (filter != null) {
                    filter.setRotation(rotation, flipHorizontal, flipVertical);
                }
            }
        }
    }

    @Override
    public void setFlipHorizontal(boolean flipHorizontal) {
        super.setFlipHorizontal(flipHorizontal);
        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            int size = mMergedFilters.size();
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                if (filter != null) {
                    filter.setFlipHorizontal(flipHorizontal);
                }
            }
        }
    }

    @Override
    public void setFlipVertical(boolean flipVertical) {
        super.setFlipVertical(flipVertical);
        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            int size = mMergedFilters.size();
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                if (filter != null) {
                    filter.setFlipVertical(flipVertical);
                }
            }
        }
    }

    @Override
    public void setRotation(Rotation rotation) {
        super.setRotation(rotation);
        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            int size = mMergedFilters.size();
            for (int i = 0; i < size; i++) {
                GPUImageFilter filter = mMergedFilters.get(i);
                if (filter != null) {
                    filter.setRotation(rotation);
                }
            }
        }
    }

    @Override
    protected void runOnDraw(Runnable runnable) {
        if (mMergedFilters != null && mMergedFilters.size() > 0) {
            mMergedFilters.get(0).runOnDraw(runnable);
        }
    }
}
